<?php
declare (strict_types = 1);

namespace app\common\middleware;
use app\common\model\user\Permission;
use Closure;
use think\Exception;
use think\exception\HttpException;
use think\Model;
use app\Request;
use think\facade\Log;

class Authority
{
    public function handle(Request $request, Closure $next)
    {

        //如果登录状态就检测权限
        if ( !app()->user->isGuest() ){
            //查询当前用户权限对比当前路由 '', 'category', 'category/index'
            $pathInfo = $this->getRealPathInfo($request);

            //获取当前登录用户拥有权限 和是否使用权限规则
            $related = Permission::getRelatedPermissions(app()->user->getIdentity()->id);

            //判断是否有权限
            $rule = $this->allow($pathInfo, $related);

            if ( $rule === false ){
                //记录日志
                Log::warning('您无权访问');
                throw new HttpException(403,'您无权访问');

            }

            //如果需要使用规则 控制器中调用回调处理
            if (!empty($rule)){
                //把规则处理方法传递给请求对象
                $request->checkRule = [
                    'callback' => [static::class, 'checkRule'],
                    'batchCallback' => [static::class, 'checkBatchRule'],
                    'listCallback' => [static::class, 'checkListRule'],
                    'rule' =>$rule
                ];
            }

            //把当前用户拥有的权限发送到控制器
            $request->related = $related;

        }else{
            //返回一个跳转响应
            return app()->user->loginRequired();
        }

        //有权限正常访问
        return $next($request);
    }

    /**
     * #获取当前请求路由信息
     * @param Request $request
     * @return string
     */
    private function getRealPathInfo(Request $request){
        return $request->completePathInfo();
    }

    /**
     * #判断是否允许当前用户访问当前路由
     * @param string $pathInfo
     * @param array $related 关联的权限
     * @return bool #允许的话返回权限规则，否则返回false
     */
    private function allow(string $pathInfo, array $related){
        //分隔符
        $ds = config('route.pathinfo_depr');

        if (empty($pathInfo) || strpos($pathInfo, $ds) === false) return false;
        $pathInfo = parse_name($pathInfo); // 转变为下划线分割格式

        //分组权限
        $pathArr = explode($ds, $pathInfo);
        $group = parse_name($pathArr[0] . $ds .'*');

        $permissions = $related['permission'] ?? [];
        $roleRule = $related['rule'] ?? 0;

        //如 category/index 或 category/* 都可以访问category/index路由
        $res =  array_key_exists($pathInfo, $permissions) ||
                array_key_exists($group, $permissions) ||
                array_key_exists('*'.$ds.'*', $permissions);

        //允许访问
        if ($res){
            //把当前规则返回
            if( $roleRule ){
                return $permissions[$pathInfo] ?? $permissions[$group] ?? $permissions['*'.$ds.'*'];
            }else{
                return null;
            }
        }

        //不允许访问
        return false;

    }

    /**
     * 检测权限规则(如 只能操作自己的内容)
     * @param Model $model 当前需要检测内容模型
     * @param int $rule 权限规则
     * @param bool $user    是否包含user外键字段
     * @return bool 允许操作返回true 否则返回false
     */
    public static function checkRule(Model $model, int $rule, bool $user = false){
        if (empty($rule)) return true;

        //匹配各种权限规则 作出相应处理
        switch ($rule){
            case Permission::RULE_PERMISSION_ALL://拥有操作所有内容的权限
                $flag = true;
                break;
            case Permission::RULE_PERMISSION_OWNER:
                //仅拥有操作自己内容的权限(没有user外键不检测直接返回true)
                $flag = $user ? $model->user_id == app()->user->getId() : true;
                break;
            //......还可以扩展更多规则
            default:
                $flag = false;
                break;
        }

        return $flag;
    }

    /**
     * 检测批量操作权限规则
     * @param string $class
     * @param int $rule
     * @param array $ids
     * @param bool $user 当前模型类是否包含user外键字段,用于检测是否是自己的内容
     * @return bool
     * @throws Exception
     */
    public static function checkBatchRule(string $class, int $rule, array $ids, bool $user = false){
        if (get_parent_class($class) !== Model::class) throw new Exception('传参错误.');
        if (empty($rule) || empty($ids)) return true;

        //匹配各种权限规则 作出相应处理
        switch ($rule){
            case Permission::RULE_PERMISSION_ALL://拥有操作所有内容的权限
                $flag = true;
                break;
            case Permission::RULE_PERMISSION_OWNER://仅拥有操作自己内容的权限
                $flag = $user ? count($ids) === $class::where([
                        ['id', 'in', $ids],
                        ['user_id','=', app()->user->getId()]
                    ])->count() : true;
                break;
            //......还可以扩展更多规则
            default:
                $flag = false;
                break;
        }
        return $flag;
    }

    /**
     * 根据权限规则生成列表条件 如：只显示自己的文章
     * @param int $rule
     * @param bool $user
     * @return array
     */
    public static function checkListRule(int $rule, bool $user = false){
        //匹配各种权限规则 作出相应处理
        switch ($rule){
            case Permission::RULE_PERMISSION_ALL://拥有操作所有内容的权限
                $where = [];
                break;
            case Permission::RULE_PERMISSION_OWNER://仅拥有操作自己内容的权限
                $where = $user ? ['user_id', '=', app()->user->getId()] : [];
                break;
            //......还可以扩展更多规则
            default:
                $where = [];
                break;
        }
        return $where;
    }



}
